#include "SQLite3.hpp"

namespace sqlite
{
using namespace std;

//Result subclass
size_t SQLite3::Result::fieldNum() const
{
    return flds.size();
}
Record SQLite3::Result::fields() const
{
    return flds;
}

size_t SQLite3::Result::recordNum() const
{
    return recs.size();
}

std::vector<Record> SQLite3::Result::records() const
{
    return recs;
}

//Cursor subclass
SQLite3::Cursor::Cursor(sqlite3* d, sqlite3_stmt* s, const SQLite3& o)
    : db(d), stmt(s), owner(o)
{}

Record SQLite3::Cursor::fetchOne()
{
    bool busy = false;
    do
    {
        int status = sqlite3_step(stmt);

        switch(status)
        {
        case SQLITE_ERROR:
        case SQLITE_MISUSE:
        {
            std::string error = sqlite3_errmsg(db);
            throw UnknownError("step failed: " +toString(status)  + error);
        }
        case SQLITE_DONE:
            return Record();
            break;
        case SQLITE_BUSY:
        case SQLITE_LOCKED:
            usleep(5000);//?
            busy = true;
            break;
        }
    }
    while (busy);
    Record rec;
    int columnNum = sqlite3_data_count(stmt);
    rec.reserve(columnNum);
    for (int i = 0; i < columnNum; i++)
    {
        if (sqlite3_column_type(stmt, i) == SQLITE_NULL)
            rec.push_back("NULL");
        else
            rec.push_back((char*) sqlite3_column_text(stmt, i));
    }
    return rec;
}

SQLite3::Cursor::~Cursor()
{
    if (stmt)
    {
        sqlite3_finalize(stmt);
    }
}

//SQLite3 Class
SQLite3::SQLite3(const string database) : db(0),transaction(false)
{
    open(database);
}

void SQLite3::open(const string database)
{
    if (sqlite3_open(database.c_str(), &db))
    {
        throw DatabaseError(sqlite3_errmsg(db));
    }
}

bool SQLite3::supportsSequences() const
{
    return false;
}

string SQLite3::getInsertID() const
{
    return toString(sqlite3_last_insert_rowid(db));
}

void SQLite3::begin() const
{
    if (!transaction)
    {
        delete execute("BEGIN;");
        transaction = true;
    }
}

void SQLite3::commit() const
{
    if (transaction)
    {
        delete execute("COMMIT;");
        transaction = false;
    }
}

void SQLite3::rollback() const
{
    if (transaction)
    {
        delete execute("ROLLBACK;");
        transaction = false;
    }
}

static int callback(void *r, int argc, char **argv, char **azColName)
{
    SQLite3::Result * res = (SQLite3::Result*) r;
    if (res->flds.size() == 0)
        for (int i = 0; i < argc; i++)
            res->flds.push_back(azColName[i]);
    Record rec;
    for (int i = 0; i < argc; i++)
        rec.push_back(argv[i] ? argv[i] : "NULL");
    res->recs.push_back(rec);
    return 0;
}

void SQLite3::throwError(int status) const
{
    string error = sqlite3_errmsg(db);
    error = toString(status) + "=status code : " + error;
    switch(status)
    {
    case SQLITE_ERROR:
        throw SQLError(error);
    case SQLITE_INTERNAL:
        throw InternalError(error);
    case SQLITE_NOMEM:
        throw MemoryError(error);
    case SQLITE_FULL:
        throw InsertionError(error);
    case  SQLITE_CONSTRAINT:
        throw ConstraintError(error);
    default:
        throw UnknownError("compile failed: " + error);
    }
}

int SQLite3::command(const string query)const
{
    char * errMsg;
    int status;
    do
    {
        status = sqlite3_exec(db, query.c_str(), 0, 0, &errMsg);
        switch(status)
        {
        case SQLITE_BUSY:
        case SQLITE_LOCKED:
            usleep(250000);
            break;
        case SQLITE_OK:
            break;
        default:
            throwError(status);
        }
    }
    while (status != SQLITE_OK);
}

bool SQLite3::tableExists(const std::string table)const
{
    //std::cout<<"tableExists..."<<std::endl;
    std::string sql="select count(*) from sqlite_master where type='table' and name='"+table+"'";
    SQLite3::Result* r=execute(sql);
    std::string result=r->recs[0][0];
    //std::cout<<result<<std::endl;
    delete r;
    return (result=="1");
}

//TODO:实现特殊字符串的替换，保证SQL字符串的安全
//替换一个单引号为两个单引号
std::string SQLite3::escapeSQL(const string str)
{
    string tmp=str;
    if (str == "NULL")	return "NULL";
    for(int i=0; i<tmp.size(); i++)
    {
        if (tmp[i]==39)
        {
            tmp.replace(i,1,"''");
            i++;
        }
    }
    return tmp;
}
SQLite3::Result* SQLite3::execute(const string query) const
{
    Result * r = new Result;
    char * errMsg;
    int status;
    do
    {
        status = sqlite3_exec(db, query.c_str(), callback, r, &errMsg);
        switch(status)
        {
        case SQLITE_BUSY:
        case SQLITE_LOCKED:
            usleep(250000);
            break;
        case SQLITE_OK:
            break;
        default:
            throwError(status);
        }
    }
    while (status != SQLITE_OK);
    return r;
}

SQLite3::Cursor* SQLite3::cursor(const string query) const
{
    while (1)
    {
        sqlite3_stmt * stmt = NULL;
        int status = sqlite3_prepare(db, query.c_str(), query.size(),
                                     &stmt, NULL);
        if (status != SQLITE_OK || stmt == NULL)
        {
            string error = sqlite3_errmsg(db);
            error = toString(status) + "=status code : " + error;
            switch(status)
            {
            case SQLITE_BUSY:
            case SQLITE_LOCKED:
                usleep(250000);
                break;
            default:
                throwError(status);
            }
        }
        else
            return new Cursor(db, stmt, *this);
    }
}

SQLite3::~SQLite3()
{
    if (db)
        sqlite3_close(db);
}

}


